#include "game.h"

#include <algorithm>
#include <chrono>
#include <iostream>

#include <GLFW/glfw3.h>
#include <thread_pool.h>

void Game::VisitAllElement(std::function<void (const GameElementPtr)> fn, bool update)
{
    std::lock_guard<std::mutex> lock(elementsMutex);
    for (const auto &ele : elements) {
        fn(ele);
    }
    if (update) {
        ThreadPool::GetInstance()->PostTask(std::bind(&Game::Update, this));
    }
}

int Game::ReadMap()
{
    lasttime = std::chrono::duration_cast<std::chrono::nanoseconds>(
        std::chrono::steady_clock::now().time_since_epoch()).count();

    std::lock_guard<std::mutex> lock(elementsMutex);
    elements.push_back(std::make_shared<UIElementTank>(UIElementLoader::GetElement("YellowTank0"), 0, 0));
    elements.push_back(std::make_shared<UIElement>(UIElementLoader::GetElement("WhiteTank0"), 0, 160));
    return 0;
}

void Game::OnKey(int32_t key, int32_t action, int32_t mods)
{
    GameElementPtr ptr = nullptr;
    {
        std::lock_guard<std::mutex> lock(elementsMutex);
        for (auto &ele : elements) {
            if (ele->IsSameElementAs(UIElementLoader::GetElement("YellowTank0"))) {
                ptr = ele;
            }
        }
    }

    if (ptr) {
        ptr->OnKey(*this, key, action, mods);
    }
}

void Game::OnMouseButton(int button, int action, int mods)
{
    GameElementPtr ptr = nullptr;
    {
        std::lock_guard<std::mutex> lock(elementsMutex);
        for (auto &ele : elements) {
            if (ele->IsSameElementAs(UIElementLoader::GetElement("YellowTank0"))) {
                ptr = ele;
            }
        }
    }

    if (ptr) {
        ptr->OnMouseButton(*this, button, action, mods);
    }
}

void Game::OnMousePosition(int x, int y)
{
    GameElementPtr ptr = nullptr;
    {
        std::lock_guard<std::mutex> lock(elementsMutex);
        for (auto &ele : elements) {
            if (ele->IsSameElementAs(UIElementLoader::GetElement("YellowTank0"))) {
                ptr = ele;
            }
        }
    }

    if (ptr) {
        ptr->OnMousePosition(*this, x, y);
    }
}

void Game::Update()
{
    std::lock_guard<std::mutex> lock(elementsMutex);
    for (auto &ele : elements) {
        ele->Update();
    }

    bool have = false;
    do {
        have = false;
        for (auto &i : elements) {
            if (i->IsSameElementAs(UIElementLoader::GetElement("Bullet0")) == false) {
                continue;
            }

            have = CheckBullet(i);
            if (have) {
                break;
            }
        }
    } while (have);

    do {
        have = false;
        for (auto &i : elements) {
            if (-1600 > i->x || i->x > 3200 || -900 > i->y || i->y > 1800) {
                if (i->IsSameElementAs(UIElementLoader::GetElement("Bullet0"))) {
                    auto func = [&](const auto &ele) { return ele == i; };
                    elements.erase(std::find_if(elements.begin(), elements.end(), func));
                    have = true;
                    break;
                } else {
                    i->angle += acos(-1);
                }
            }
        }
    } while (have);
}

bool Game::CheckBullet(const GameElementPtr &bullet)
{
    for (auto &j : elements) {
        if (bullet != j && bullet->IsIntersectWith(*j)) {
            if (j->canDestroy) {
                auto func = [&](const auto &ele) { return ele == bullet || ele == j; };
                elements.erase(std::find_if(elements.begin(), elements.end(), func));
                elements.erase(std::find_if(elements.begin(), elements.end(), func));
            } else if (j->canBlock) {
                auto func = [&](const auto &ele) { return ele == bullet; };
                elements.erase(std::find_if(elements.begin(), elements.end(), func));
            }
            return true;
        }
    }
    return false;
}
